package moltencore.network.protocol;

import spark.network.protocol.Response;
import spark.network.protocol.Base;
import spark.network.Util;

import java.util.LinkedList;
import java.nio.ByteBuffer;

public class JoinGameResponse extends Response<JoinGame> {
	private static final Class<JoinGame> REQUEST_TYPE_CLASS = JoinGame.class;

	public static final boolean RELIABLE = true;
	public static final int MESSAGE_LENGTH_MAX = Byte.MAX_VALUE;

	public static final short TYPE = 0x41;
	private static final byte[] TYPE_BYTES = Util.makeBytes(TYPE);

	/**
	 * 0    if we declined the join for whatever reason
	 * !=0  if we accepted
	 */
	private byte accepted;
	private static final int ACCEPTED_LENGTH = Byte.SIZE / 8;

	/**
	 * Client id assigned to the player
	 */
	private long clientId;
	private static final int CLIENT_ID_LENGTH = Long.SIZE / 8;

	/**
	 * Length of the message contained within this package
	 */
	private byte messageLength;
	private static final int MESSAGE_LENGTH_LENGTH = Byte.SIZE / 8;

	/**
	 * Reason for rejection or something else we want to tell the client.
	 */
	private byte[] message;

	private static final int JOINED_PLAYERS_LENGTH_LENGTH = Byte.SIZE / 8;

	private LinkedList<PlayerData> joinedPlayers = new LinkedList<PlayerData>();

	private static final int SIZE =
			Response.SIZE +
			ACCEPTED_LENGTH +
			CLIENT_ID_LENGTH +
			MESSAGE_LENGTH_LENGTH +
			JOINED_PLAYERS_LENGTH_LENGTH;

	public JoinGameResponse() {
		super(RELIABLE, TYPE_BYTES, REQUEST_TYPE_CLASS);
	}

	public JoinGameResponse(JoinGame query) {
		super(RELIABLE, TYPE_BYTES, query, REQUEST_TYPE_CLASS);
	}

	public void setAccepted(boolean accepted) {
		this.accepted = (byte)((accepted) ? 1 : 0);
	}

	public boolean getAccepted() {
		return accepted != 0;
	}

	public byte[] getMessage() {
		return message;
	}

	public long getClientId() {
		return clientId;
	}

	public void setClientId(long clientId) {
		this.clientId = clientId;
	}

	public LinkedList<PlayerData> getJoinedPlayers() {
		return joinedPlayers;
	}

	public void setMessage(String message) {
		byte[] messageBytes = message.getBytes();
		//set the length of the messageLength to the length of the provided string or to the maximal allowed length if the strings
		//length exceeds the maximum length
		messageLength = (byte) ((messageBytes.length > MESSAGE_LENGTH_MAX) ? MESSAGE_LENGTH_MAX : messageBytes.length);

		//write nameLength characters into the name byte array
		this.message = new byte[messageLength];
		System.arraycopy(messageBytes, 0, this.message, 0, messageLength);
	}

	@Override
	public void getBytes(byte[] buffer) {
		super.getBytes(buffer);
		ByteBuffer bb = ByteBuffer.wrap(buffer);
		bb.position(Response.SIZE);
		bb.put(accepted);
		bb.putLong(clientId);
		bb.put(messageLength);
		if(message != null) {
			bb.put(message);
		}
		bb.put((byte)joinedPlayers.size());
		for(PlayerData playerData : joinedPlayers) {
			playerData.getBytes(buffer, bb.position());
			bb.position(bb.position() + playerData.getSize());
		}
	}

	@Override
	public void fill(byte[] buffer) {
		super.fill(buffer);
		ByteBuffer bb = ByteBuffer.wrap(buffer);
		bb.position(Response.SIZE);
		accepted = bb.get();
		clientId = bb.getLong();
		messageLength = bb.get();
		message = new byte[messageLength];
		bb.get(message);
		int joinedPlayersCount = bb.get();
		for(int i = 0; i < joinedPlayersCount; i++) {
			PlayerData playerData = new PlayerData();
			playerData.fill(buffer, bb.position());
			bb.position(bb.position() + playerData.getSize());
			joinedPlayers.add(playerData);
		}
	}

	@Override
	public int getSize() {
		int size = SIZE + messageLength;
		for(PlayerData playerData : joinedPlayers) {
			size += playerData.getSize();
		}
		return size;
	}

	public static JoinGameResponse create(byte[] buffer, long receiveTime) {
		JoinGameResponse packet = new JoinGameResponse();
		packet.fill(buffer);
		return packet;
	}
}